package com.jml.jdk8.stream;


import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;

/**
 * @author:jml
 * @date: 2020/2/11 0011 12:32
 * @description：
 */
public class Collector和Comparator {

    public static Student student1 = new Student("zhangsan", 80);
    public static Student student2 = new Student("lisi", 90);
    public static Student student3 = new Student("wangwu", 100);
    public static Student student33 = new Student("wangwu", 99);
    public static Student student4 = new Student("zhaoliu", 90);
    public static Student student5 = new Student("zhaoliu", 90);
    public static List<Student> students = Arrays.asList(student1, student2, student3, student4);
    public static List<Student> students1 = Arrays.asList(student1, student2, student3, student4,student5,student33);

    @Test
    public void 通过流的方式来得到list的数量(){
        List<Student> students1 = students.stream().collect(Collectors.toList());
        students1.forEach(System.out::println);
        System.out.println("------------------------------------------------------------");
        //通过流的方式来得到list的数量
        Long count = students.stream().collect(Collectors.counting());
        long count1 = students.stream().count();
        System.out.println("数量count:"+count);
        System.out.println("数量count1:"+count1);
    }

    /**
     * 找出集合中分数最低或者最大的然后打印出来
     * minBy指的是最小的通过comparator条件
     * maxBy指的是最大的通过comparator条件
     * averaging指的平局值
     * summingInt得到总数
     * summarizingInt得到总结，里面有各种值，最大最小平均个数，IntStream类中也有方法summaryStatistics也是得到总结
     * joining 拼接字符，可以加分隔符和前后缀
     */
    @Test
    public void minByMaxBy找出集合中分数最低同学(){
        //minBy
        students.stream().collect(Collectors.minBy
                (Comparator.comparingInt(Student::getScore))).ifPresent(System.out::println);
        //maxBy
        students.stream().collect(Collectors.maxBy
                (Comparator.comparingInt(Student::getScore))).ifPresent(System.out::println);
        //平均值
        Double average = students.stream().collect(Collectors.averagingDouble(Student::getScore));
        System.out.println(average);
        //总结summarizingInt-
        //总和summingInt
        Integer sum1 = students.stream().collect(Collectors.summingInt(Student::getScore));
        System.out.println("--"+sum1);
        IntSummaryStatistics sum = students.stream().collect(Collectors.summarizingInt(Student::getScore));
        System.out.println(sum.getSum());
        System.out.println(sum.getMax());
        System.out.println(sum.getAverage());
        System.out.println(sum.getCount());
        System.out.println(sum.getMin());
        //把名字拼接起来,如果要用分隔符拼接就直接在里面加
        //joining拼接起来后还可以加前后缀
        String joinName = students.stream().map(Student::getName).collect(Collectors.joining());
        String joinName加分割符 = students.stream().map(Student::getName).collect(Collectors.joining(","));
        String joinName加前后缀 = students.stream().map(Student::getName).collect(Collectors.joining(" 分隔符 ","前缀","后缀"));
        System.out.println(joinName加前后缀);
    }

    /**
     * 分组：根据分数，然后再根据名字进行分组.
     * 根据分数分组有三种80.90.100，再根据名字90里面有两个赵六和一个李四
     * key是以及分组分数，value又是一个map，因为有分组了，这个map的key就是String，value就是个集合list。
     * 三次分组，分组顺序 分数->名字->年龄
     * Map<Integer, Map<String, Map<int ,List<Student>>
     * 两次分组，分组顺序 分数->名字
     * Map<Integer, Map<String, List<Student>>>
     * 一次分组
     * Map<Integer, List<Student>>
     */
    @Test
    public void 多次分组(){
        Map<Integer, Map<String, List<Student>>> group2Map = students1.stream().collect(
                Collectors.groupingBy(Student::getScore, Collectors.groupingBy(Student::getName)));
    }

    /**
     * collectingAndThen方法有两个参数一个是Collector,一个是Function,先得到Collectord的结果然后把结果作为function的参数。
     * 先计算第一个参数，再计算第二个参数
     */
    @Test
    public void 分区和分组混合使用collectingAndThen(){
        //两次分区
        Map<Boolean, Map<Boolean, List<Student>>> partitioningBy2 = students1.stream().collect(Collectors
                .partitioningBy(s -> s.getScore() >= 90,
                        Collectors.partitioningBy(s -> s.getAge() >= 10)));
        //先分组，再分区，再分组
        Map<Integer, Map<Boolean, Map<String, List<Student>>>> partitioningByGroupingBy = students1.stream()
                .collect(
                        Collectors.groupingBy(Student::getScore,
                                Collectors.partitioningBy(s -> s.getScore() >= 90,
                                        Collectors.groupingBy(s -> s.getName()))));
        System.out.println(partitioningByGroupingBy);
        //统计分数大于80的学生，取出这个 个分组总的个数
        Map<Boolean, Long> collect = students1.stream()
                .collect(Collectors.partitioningBy(s -> s.getScore() >= 80, Collectors.counting()));
        System.out.println(collect);
        //先以名字分组，取出每个组中分数最小的学生
        //collectingAndThen
        Map<String, Student> collectingAndThen = students1.stream()
                .collect(Collectors.groupingBy
                        (Student::getName,
                                Collectors.collectingAndThen
                                        (Collectors.minBy(Comparator.comparingInt(Student::getScore)),Optional::get)));
        System.out.println("collectingAndThen--:   "+collectingAndThen);
    }

    /**
     * 1.默认的排序规则,按照字母的升序排序
     * 2.按照字符串的长度升序进行排序
     * 3.按照字符串长度降序进行排序
     */
    @Test
    public void comparatorComparingInt(){
        List<String> list = Arrays.asList("nihao", "hello", "world", "welcome");
        //这是默认的排序规则,按照字母的升序排序
        //Collections.sort里面调的就是list.sort()
        Collections.sort(list);
        System.out.println(list);
        //按照字符串的长度升序进行排序
        Collections.sort(list,(item1,item2) -> item1.length()-item2.length());
        System.out.println(list);
        //按照字符串长度降序进行排序
        Collections.sort(list,(item1,item2) -> item2.length()-item1.length());
        System.out.println(list);
        //comparingInt使用具体的类型是Comparator接口的的静态方法,参数是IntFunction
        //String::length使用方法引用就成功了，但是不能直接用item -> item.length()，
        //   因为这里默认是Object，Object是没有length方法的，所以要表明具体类型(String item) -> item.length()
        //方法定义：public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor)
        //      在这里Comparator<T>的T就是String类型，ToIntFunction的参数表明是T及其T的父类，所以编译器认为这个类型是Object
        //      如果去掉reversed()编译器就可以识别参数，因为这个时候comparingInt直接返回了第二个参数，离上下文更近
        //      加上reversed()，reversed()的返回值Comparator就会作为第二个参数，reversed()之前的东西离上下文更远，所以推断不出
        //默认是使用升序，如果要使用降序就在后面加reversed
        Collections.sort(list,Comparator.comparingInt(String::length).reversed());
        list.sort(Comparator.comparingInt(String::length).reversed());
        //Comparator.comparingInt离上下文远
        Collections.sort(list,Comparator.comparingInt((String item) -> item.length()).reversed());
        //Comparator.comparingInt离上下文进
        Collections.sort(list,Comparator.comparingInt(item -> item.length()));
        System.out.println(list);
    }


    /**
     * 多次排序：按照长度排序，再按照字母排序
     * 字母排序：区分和不区分大小写
     * thenComparing什么时候起作用：当前一个比较器认为是相等的时候返回值为0，就会用thenComparing中的比较器，否则不会用
     */
    @Test
    public void thenComparing和CASE_INSENSITIVE_ORDER和Comparing多个比较器(){
        List<String> list = Arrays.asList("nihao", "hello", "world", "welcome");
        //先按照长度排序，再按照字母排序
        Collections.sort(list,Comparator.comparingInt(String::length)
                .thenComparing((item1,item2)-> item1.compareToIgnoreCase(item2)));
        System.out.println("list: "+list);

        //先按照长度排序,不区分大小写排序,直接用
        List<String> list1 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list1,Comparator.comparingInt(String::length)
                 .thenComparing(String.CASE_INSENSITIVE_ORDER));
        System.out.println("list1: "+list1);

        //先按照长度排序,不区分大小写排序,用lambda表达式
        List<String> list2 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list2,Comparator.comparingInt(String::length)
                .thenComparing((item1,item2)->item1.compareToIgnoreCase(item2)));
        System.out.println("list2: "+list2);

        //不区分大小写排序,用方法引用
        List<String> list3 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list3,Comparator.comparingInt(String::length)
                .thenComparing(Comparator.comparing(String::toLowerCase)));
        System.out.println("list3: "+list3);

        //不区分大小写排序，然后在进行倒序
        //thenComparing后面还可以一直接thenComparing
        //Comparator.comparing一个参数是function，两个参数是Comparator，使用完前面的比较器后再实现第二个参数的比较器
        List<String> list4 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list4,Comparator.comparingInt(String::length)
                .thenComparing(Comparator.comparing(String::toLowerCase,Comparator.reverseOrder()))
                /*.thenComparing()*/);
        System.out.println("list4: "+list4);

        //先长度进行逆序(大-小)，再按字母不区分大小写逆序(z-a)排序
        //reversed本质就是reverseOrder，reverseOrder是静态方法，reversed是实例方法
        List<String> list5 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list5,Comparator.comparingInt(String::length).reversed()
                .thenComparing(Comparator.comparing(String::toLowerCase,Comparator.reverseOrder())));
        System.out.println("list5: "+list5);

        // 原始数据 "nihao" , "hello", "world", "welcome"  对字符长度进行逆序
        //"welcome, nihao ,  hello ,  world" ，  对nihao ,  hello ,  world字母进行逆序
        //"welcome, world  ,  hello , nihao"  因为上一步字母排序返回值都不为0，所以最后一个thenComparing就没有生效
        //
        List<String> list6 = Arrays.asList("nihao", "hello", "world", "welcome");
        Collections.sort(list6,Comparator.comparingInt(String::length).reversed()
                .thenComparing(Comparator.comparing(String::toLowerCase,Comparator.reverseOrder()))
                .thenComparing(Comparator.reverseOrder())
        );
        System.out.println("list6: "+list6);

    }

}
